/*
 * Stok3d - getting you stoked on Flash Player native 3D
 * Copyright (C) 2009  David Lenaerts
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Author information:
 * David Lenaerts
 * url: http://www.derschmale.com
 * mail: david.lenaerts@gmail.com
 * 
 */

package com.derschmale.stok3d.filters
{
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Shader;
	import flash.geom.Matrix3D;
	import flash.geom.Rectangle;
	
	/**
	 * The ParallaxFilter class creates depth on display objects by displacing the texture in order to create
	 * a parallax effect. It works best when used as the last filter in the DisplayObject::filters Array.
	 */
	public class ParallaxFilter extends PixelShader3D
	{
		[Embed(source="/../pbj/ParallaxShader.pbj", mimeType="application/octet-stream")]
		private var Kernel : Class;
		
		private var _inverse : Matrix3D = new Matrix3D();
		private var _bias : Number;
		private var _scale : Number;
		private var _numSteps : Number;
		
		/**
		 * Create a ParallaxFilter instance.
		 * 
		 * @param heightMap A BitmapData object representing the height of the surface at a certain position
		 * @param normalMap A normal map in tangent space.
		 * @param numSteps The amount of iterations performed to create the parallax effect.
		 * @param bias The average offset on the normal map.
		 * @param scale The amount of displacement that occurs on the texture.
		 */
		public function ParallaxFilter(heightMap : BitmapData, normalMap : BitmapData, numSteps : int = 5, bias : Number = -10, scale : Number = 30)
		{
			super(new Shader(new Kernel()));
			shader.data.heightMap.input = heightMap;
			shader.data.normalMap.input = normalMap;
			this.numSteps = numSteps;
			this.bias = bias;
			this.scale = scale;
		}
		
		/**
		 * The amount of iterations performed to create the parallax effect. Higher values look better, but are slower.
		 */
		public function get numSteps() : Number
		{
			return _numSteps;
		}
		
		public function set numSteps(value : Number) : void
		{
			_numSteps = value;
			shader.data.numSteps.value = [ value ];
			shader.data.bias.value = [ _bias/value ];
			shader.data.scale.value = [_scale/value ];
		}
		
		/**
		 * The average offset on the normal map.
		 */
		public function get bias() : Number
		{
			return _bias;
		}
		
		public function set bias(value : Number) : void
		{
			_bias = value;
			shader.data.bias.value = [ value/_numSteps ];
		}
		
		/**
		 * The amount of displacement that occurs on the texture. It can be seen as the relief of the surface.
		 */
		public function get scale() : Number
		{
			return _scale;
		}
		
		public function set scale(value : Number) : void
		{
			_scale = value;
			shader.data.scale.value = [ value/_numSteps ];
		}

		
		override public function update(target:DisplayObject):void
		{
			var bounds : Rectangle = target.getBounds(target);
			var raw : Vector.<Number> = target.transform.matrix3D.rawData;
			raw[12] -= target.stage.stageWidth*.5;
			raw[13] -= target.stage.stageHeight*.5;
			
			shader.data.transformation.value = [ 	raw[0], raw[1], raw[2], raw[3], 
													raw[4], raw[5], raw[6], raw[7], 
													raw[8], raw[9], raw[10], raw[11], 
													raw[12], raw[13], raw[14], raw[15]
												];
			
			if (raw[10] > 0)
				shader.data.tangentTransformation.value = [ raw[0], raw[1], raw[2], 
															raw[4], raw[5], raw[6], 
															-raw[8], -raw[9], -raw[10]
															];
			else
				shader.data.tangentTransformation.value = [ raw[0], raw[1], raw[2], 
															raw[4], raw[5], raw[6], 
															raw[8], raw[9], raw[10]
															];
			shader.data.upperLeft.value = [ bounds.x, bounds.y ];								
			
			
		}
	}
}